package com.zx._09_架构.rpc演变过程.rpc_06;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;
import lombok.extern.slf4j.Slf4j;

//  优化1：Stub 是 consumer 端的代理对象，负责网络的连接、请求数据发送、响应接受、数据封装等
//  优化2：进阶为动态代理
//  优化3：将请求的参数，改为需要调用的方法和方法参数，这样可以通过代理对象调用多个方法
//  优化4：优化返回值类型，直接使用object对象进行网络传输
//  优化5：可以调用任意接口的方法
@Slf4j
public class Stub {

    public static <T> T getProxy(Class<T> clazz) {
        if (clazz.isInterface()) {
            log.info("调用是接口");
        }
        // 动态代理，需要去执行的代码
        InvocationHandler h = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy,
                    Method method, /*#调用方法的名字#*/
                    Object[] args  /*#请求的参数#*/) throws Throwable {

                Socket socket;
                try {
                    socket = new Socket("localhost", 8888);
                    ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());

                    String className = clazz.getName();
                    String methodName = method.getName();
                    Class<?>[] parameterTypes = method.getParameterTypes();

                    // 请求的参数修改为需要调用的方法及参数
                    output.writeUTF(className);
                    output.writeUTF(methodName);
                    output.writeObject(parameterTypes);
                    output.writeObject(args);
                    output.flush();

                    ObjectInputStream input = new ObjectInputStream(socket.getInputStream());

                    Object readObject = input.readObject();
                    socket.close();
                    return readObject;
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            }
        };

        Object object = Proxy.newProxyInstance(clazz.getClassLoader(),
                new Class[]{clazz}, h);
        return (T) object;
    }

}
